#include "../../core/pch.h"
#include "../../pi/BinarySemaphore.h"

/** Variable for statuses of sockets functions. */
int status;

/**
 * Semaphore for mutual exclusive access to 
 * logger_sock shared by logger and worker. 
 */
int sem_id;

int logger_sock;
int receiver_sock;
int worker_sock;

int worker_port;
int receiver_port;

/** Coordinator ip. */
char* coordIp;

/** For algorithm purposes. */
unsigned int N, M, p;

int main(int argc, char* argv[])
{
	DEBUGC("worker: started working");

	logger_sock   = atoi(argv[1]);
	worker_port   = atoi(argv[2]);
	sem_id 		  =	atoi(argv[3]);
	receiver_port = atoi(argv[4]);
	coordIp		  = argv[5];

	FILL_SOCKADDR_IN(receiver_addr, AF_INET, receiver_port, coordIp);
	FILL_SOCKADDR_IN(worker_addr, AF_INET, worker_port, INADDR_ANY);

	/** Create listening socket for worker. */
	worker_sock = socket(AF_INET, SOCK_STREAM, 0); 
	if (worker_sock == SOCKET_ERROR)
	{
		DEBUGC("worker: error while creating listening socket for worker (%s)", strerror(errno));
		exit(SOCKET_ERROR);
	}

	status = bind(worker_sock, (struct sockaddr*) &worker_addr, sizeof(worker_addr)); 
	if (status == BIND_ERROR)
	{
		DEBUGC("worker: error while binding socket to address (%s)", strerror(errno));
		exit(BIND_ERROR);
	}

	status = listen(worker_sock, 64); 
	if (status == LISTEN_ERROR)
	{
		DEBUGC("worker: error while trying to listen on socket (%s)", strerror(errno));
		exit(LISTEN_ERROR);
	}

	struct sockaddr_in cliaddr;
	socklen_t len = sizeof(cliaddr);

	char msg;
	while (true)
	{
		DEBUGC("worker: waiting for request");

		int sender_sock = accept(worker_sock, (struct sockaddr*) &cliaddr, &len);
		if (sender_sock == ACCEPT_ERROR)
		{
			DEBUGC("worker: error while accepting connection (%s)", strerror(errno));
			exit(ACCEPT_ERROR);
		}

		DEBUGC("worker: accepted request");

		/** Receive type of work request. */
		recv(sender_sock, &msg, sizeof(char), 0);

		int times_count;
		switch (msg)
		{
			case FACTOR_REQUEST:
				/** Receiver data for algorithm. */
				recv(sender_sock, &N, sizeof(unsigned int), 0);
				recv(sender_sock, &p, sizeof(unsigned int), 0);

				close(sender_sock);

				DEBUGC("FACTOR_REQUEST: N = %d, p = %d", N, p);
				if (N == 1) exit(0);

				/** Start the algorithm. */
				times_count = 0;
				M = N;
				while ((N % p) == 0 && p > 1)
				{
					N = N / p;
					times_count++;
				}

				/** Send back the results. */
				receiver_sock = socket(AF_INET, SOCK_STREAM, 0);
				if (receiver_sock == SOCKET_ERROR)
				{
					DEBUGC("worker: error while creating socket for receiver to send back results (%s)",
						strerror(errno));
					exit(SOCKET_ERROR);
				}

				status = connect(receiver_sock, (struct sockaddr*) &receiver_addr, sizeof(receiver_addr));
				if (status == CONNECT_ERROR)
				{
					DEBUGC("worker: error while connecting to receiver (%s)", strerror(errno));
					exit(CONNECT_ERROR);
				}

				send(receiver_sock, &M, sizeof(unsigned int), 0);
				send(receiver_sock, &p, sizeof(unsigned int), 0);
				send(receiver_sock, &times_count, sizeof(int), 0);
				close(receiver_sock);

				/** Signalise idleness. */
				msg = IM_IDLE;
				BinarySemaphore::Decrement(sem_id);
				send(logger_sock, &msg, sizeof(char), 0);
				BinarySemaphore::Increment(sem_id);

				break;
			default:
				DEBUGC("worker: not recognized protocol message (%d), exitinig", msg);
				exit(STATUS_ERR);
				break;
		}
	}

	return 0;
}

